home *** CD-ROM | disk | FTP | other *** search
- /*
- * WRay_Pick.c
- *
- * QuickDraw 3D 1.6 Sample
- * Robert Dierkes
- *
- * This sample code simpulates a bouncing ball. It is a simplification
- * of this, however, in that it only tests a single ray against neighboring
- * objects for collisions. The world ray pick origin is positioned at the
- * center of the ball and the direction vector controls is direction.
- * Sometimes portions of the animated ball pass through objects because
- * the hit testing is essentially done from the front of the ball and not
- * several points along its surface.
- *
- * 07/28/98 RDD Created.
- */
-
- /*------------------*/
- /* Include Files */
- /*------------------*/
- #include "QD3D.h"
- #include "QD3DMath.h"
- #include "QD3DGroup.h"
- #include "QD3DPick.h"
- #include "QD3DTransform.h"
- #include "QD3DView.h"
-
- #include "WRay_Error.h"
- #include "WRay_Document.h"
- #include "WRay_Memory.h"
- #include "WRay_Scene.h"
- #include "WRay_System.h"
- #include "WRay_Pick.h"
-
- #include <math.h>
-
-
-
- /*------------------*/
- /* Constants */
- /*------------------*/
- #define kBallRadius (kCylRadius * 0.95f)
- #define kMinimumDistance (kBallRadius * 0.956f)
-
- #define kFirstHit 0
-
- /*------------------*/
- /* Macros */
- /*------------------*/
- /* ValidMask Macros */
- #define HAS_Object(validMask) ((validMask & kQ3PickDetailMaskObject) !=0)
- #define HAS_Distance(validMask) ((validMask & kQ3PickDetailMaskDistance)!=0)
- #define HAS_XYZ(validMask) ((validMask & kQ3PickDetailMaskXYZ)!=0)
- #define HAS_Normal(validMask) ((validMask & kQ3PickDetailMaskNormal)!=0)
-
-
- /*----------------------*/
- /* Global Declarations */
- /*----------------------*/
- static TQ3PickObject gPick = NULL;
- static TQ3Ray3D gWorldRay;
- static TQ3Vector3D gWorldDelta;
-
- static TQ3GroupPosition gBallGroupPosn = NULL;
- static TQ3GeometryObject gBallGeo = NULL;
- static TQ3GeometryObject gCurGeoHit = NULL;
- static TQ3GroupObject gLineGroup = NULL;
-
- TQ3ColorRGB rgbColors[] = {
- { 1.0, 1.0, 1.0 }, /* k_White */
- { 1.0, 0.0, 0.0 }, /* k_Red */
- { 0.0, 1.0, 0.0 }, /* k_Green */
- { 0.0, 0.0, 1.0 }, /* k_Blue */
- { 1.0, 1.0, 0.0 }, /* k_Yellow */
- { 0.0, 1.0, 1.0 }, /* k_Cyan */
- { 1.0, 0.0, 1.0 }, /* k_Magenta*/
- { 1.0, 0.5, 0.0 } /* k_Orange */
- };
-
- enum {
- k_White,
- k_Red,
- k_Green,
- k_Blue,
- k_Yellow,
- k_Cyan,
- k_Magenta,
- k_Orange
- };
-
-
- /*----------------------*/
- /* Local Prototypes */
- /*----------------------*/
- static
- TQ3PickObject Pick_New(
- TQ3Ray3D *pWorldRay);
- static
- TQ3Status Pick_Document(
- TDocumentPtr pDoc,
- TQ3PickObject pickObj,
- unsigned long *pNumHits);
-
- static
- TQ3Status Pick_InitializeWorldRay(
- TQ3PickObject pickObj,
- TQ3Ray3D *pWorldRay,
- TQ3Vector3D *pWorldDelta);
- static
- TQ3Status Pick_ComputeReflectedWorldRay(
- TQ3PickObject pickObj,
- TQ3Ray3D *pWorldRay,
- TQ3Vector3D *pWorldDelta);
- static
- TQ3GeometryObject Pick_CreateMovingObject(
- TDocumentPtr pDoc,
- TQ3Ray3D *pRay,
- TQ3GroupPosition *pGroupPosn);
- static
- TQ3Status Pick_GetNearestObject(
- TQ3PickObject pickObj,
- float minDistance,
- TQ3GeometryObject *pGeoHit,
- TQ3GeometryObject *pPrevGeoHit);
- static
- TQ3Status Pick_HighlightObject(
- TDocumentPtr pDoc,
- TQ3GeometryObject geoObj,
- TQ3Boolean doHighlight);
-
- TQ3GeometryObject Pick_CreateLine(
- TQ3Point3D *pXYZ,
- TQ3Vector3D *pNormal,
- float scale,
- unsigned long colorIndex);
-
- TQ3Status Pick_DrawRayAndNormal(
- TDocumentPtr pDoc,
- TQ3Ray3D *pTestRay,
- TQ3GroupObject normalGroup,
- unsigned long numHits);
-
-
- /*
- * Pick_Initialize
- */
- TQ3Boolean Pick_Initialize(
- void)
- {
- TQ3Status status = kQ3Failure;
-
- gPick = NULL;
- gBallGroupPosn = NULL;
- gBallGeo = NULL;
- gCurGeoHit = NULL;
-
- Q3Point3D_Set(&gWorldRay.origin, 0.0, 0.0, 0.0);
- Q3Vector3D_Set(&gWorldRay.direction, 0.0, 0.0, 1.0);
-
- gPick = Pick_New(&gWorldRay);
-
- return (gPick != NULL) ? kQ3True : kQ3False;
- }
-
-
- /*
- * Pick_Exit
- */
- TQ3Boolean Pick_Exit(
- void)
- {
- Object_Dispose_NULL(&gPick);
- Object_Dispose_NULL(&gBallGeo);
- Object_Dispose_NULL(&gCurGeoHit);
- Object_Dispose_NULL(&gLineGroup);
-
- return kQ3True;
- }
-
-
- #pragma mark -
-
- /*
- * Pick_New
- */
- TQ3PickObject Pick_New(
- TQ3Ray3D *pWorldRay)
- {
- TQ3WorldRayPickData rayPickData;
- TQ3PickObject pickObject = NULL;
-
- /*
- * Tolerance values are only used for Point, Line, Ellipse,
- * NURB Curve, PolyLine, Mesh geometries and are measured
- * in world space.
- */
- #define kVertexTolerance 0.01
- #define kEdgeTolerance 0.005
-
- rayPickData.data.sort = kQ3PickSortNearToFar;
-
- rayPickData.data.mask = kQ3PickDetailMaskObject |
- kQ3PickDetailMaskXYZ |
- kQ3PickDetailMaskDistance |
- kQ3PickDetailMaskNormal;
- rayPickData.data.numHitsToReturn = 1;
-
- /* Make sure ray is normalized */
- Q3Vector3D_Normalize(&pWorldRay->direction, &pWorldRay->direction);
- rayPickData.ray = *pWorldRay;
-
- rayPickData.vertexTolerance = kVertexTolerance;
- rayPickData.edgeTolerance = kEdgeTolerance;
-
- /* Create the new world ray pick object */
- pickObject = Q3WorldRayPick_New(&rayPickData);
- DEBUG_ASSERT(pickObject != NULL, Q3WorldRayPick_New);
-
- return pickObject;
- }
-
-
- /*
- * Pick_Document
- */
- static
- TQ3Status Pick_Document(
- TDocumentPtr pDoc,
- TQ3PickObject pickObj,
- unsigned long *pNumHits)
- {
- TQ3Status status = kQ3Success;
- TQ3ViewStatus viewStatus = kQ3ViewStatusError;
-
- if (Q3View_StartPicking(pDoc->fView, pickObj) == kQ3Failure) {
- ERROR_DEBUG_STR("Pick_Document: Q3View_StartPicking failed.");
- return kQ3Failure;
- }
-
- do {
- Document_Submit_Objects(pDoc, pDoc->fView);
-
- viewStatus = Q3View_EndPicking(pDoc->fView);
- }
- while (viewStatus == kQ3ViewStatusRetraverse);
-
- DEBUG_ASSERT(viewStatus == kQ3ViewStatusDone, Pick_Document);
-
- status = Q3Pick_GetNumHits(pickObj, pNumHits);
-
- return status;
- }
-
-
- #pragma mark -
-
- /*
- * Pick_GetIsAnimating
- */
- TQ3Boolean Pick_GetIsAnimating(
- void)
- {
- return (gBallGeo != NULL) ? kQ3True : kQ3False;
- }
-
-
- /*
- * Pick_SetIsAnimating
- */
- TQ3Status Pick_SetIsAnimating(
- TDocumentPtr pDoc,
- TQ3Boolean newIsAnimating)
- {
- if (newIsAnimating == Pick_GetIsAnimating()) {
- return kQ3Failure;
- }
-
- return (newIsAnimating) ? Pick_BeginAnimation(pDoc)
- : Pick_EndAnimation(pDoc);
- }
-
-
- #pragma mark -
-
- /*
- * Pick_BeginAnimation
- */
- TQ3Status Pick_BeginAnimation(
- TDocumentPtr pDoc)
- {
- TQ3Status status = kQ3Failure;
-
- if (Pick_GetIsAnimating() == kQ3True) {
- /* Animation already on */
- return status;
- }
-
- /* Setup initial world ray origin and direction */
- status = Pick_InitializeWorldRay(gPick, &gWorldRay, &gWorldDelta);
- if (status == kQ3Failure) {
- return status;
- }
-
- /* Create ball */
- gBallGeo = Pick_CreateMovingObject(pDoc, &gWorldRay, &gBallGroupPosn);
- DEBUG_ASSERT(gBallGeo != NULL, Pick_CreateMovingObject);
- if (gBallGeo == NULL) {
- return kQ3Failure;
- }
-
- gLineGroup = Q3OrderedDisplayGroup_New();
- DEBUG_ASSERT(gLineGroup != NULL, Q3OrderedDisplayGroup_New);
-
- return status;
- }
-
-
- /*
- * Pick_EndAnimation
- */
- TQ3Status Pick_EndAnimation(
- TDocumentPtr pDoc)
- {
- if (Pick_GetIsAnimating() == kQ3False) {
- /* Animation not on */
- return kQ3Failure;
- }
-
- Object_Dispose_NULL(&gLineGroup);
-
- /* Unhighlight anything that's still highlighted */
- if (gCurGeoHit != NULL) {
- /* Unhighlight the ball & current geometry */
- Pick_HighlightObject(pDoc, gBallGeo, kQ3False);
- Pick_HighlightObject(pDoc, gCurGeoHit, kQ3False);
- Object_Dispose_NULL(&gCurGeoHit);
- }
- Object_Dispose_NULL(&gBallGeo);
-
- /* Remove temporary ball group from main group */
- if (gBallGroupPosn != NULL) {
- DEBUG_ASSERT(gCurGeoHit == NULL, Pick_EndAnimation);
- gCurGeoHit = Q3Group_RemovePosition (pDoc->fModel, gBallGroupPosn);
- Object_Dispose_NULL(&gCurGeoHit);
- gBallGroupPosn = NULL;
- }
-
- Document_Draw(pDoc);
-
- return kQ3Success;
- }
-
-
- /*
- * Pick_Animate
- *
- * Move ball through the scene avoiding objects using
- * ray picking to check for geometries in its path.
- */
- TQ3Status Pick_Animate(
- TDocumentPtr pDoc)
- {
- TQ3Status status = kQ3Failure;
- unsigned long numHits;
- TQ3GeometryObject prevGeoHit = NULL;
-
- if (Pick_GetIsAnimating() == kQ3False) {
- /* If animation hasn't started then do nothing */
- return kQ3Failure;
- }
-
- /* Submit scene for picking */
- status = Pick_Document(pDoc, gPick, &numHits);
- DEBUG_ASSERT(status == kQ3Success, Pick_Document);
-
- if (numHits > 0) {
- status = Pick_GetNearestObject(gPick, kMinimumDistance, &gCurGeoHit, &prevGeoHit);
-
- if (Scene_GetShowLines()) {
- Pick_DrawRayAndNormal(pDoc, &gWorldRay, gLineGroup, numHits);
- }
-
- if (gCurGeoHit != prevGeoHit) {
- if (prevGeoHit != NULL) {
- /* Unhighlight the ball & previous geometry */
- Pick_HighlightObject(pDoc, gBallGeo, kQ3False);
- Pick_HighlightObject(pDoc, prevGeoHit, kQ3False);
- Object_Dispose_NULL(&prevGeoHit);
- }
-
- if (gCurGeoHit != NULL) {
- /* Highlight the ball & the new closest geometry*/
- Pick_HighlightObject(pDoc, gBallGeo, kQ3True);
- Pick_HighlightObject(pDoc, gCurGeoHit, kQ3True);
-
- /* status = */
- Pick_ComputeReflectedWorldRay(gPick, &gWorldRay, &gWorldDelta);
-
- System_Sound();
- }
- }
- }
- else {
- /* Nothing hit */
- if (gCurGeoHit != NULL) {
- /* Unhighlight the ball & current geometry */
- Pick_HighlightObject(pDoc, gBallGeo, kQ3False);
- Pick_HighlightObject(pDoc, gCurGeoHit, kQ3False);
- Object_Dispose_NULL(&gCurGeoHit);
- }
- }
-
- Q3Pick_EmptyHitList(gPick);
-
- /* Advance ball by delta amount */
- Q3Point3D_Vector3D_Add(&gWorldRay.origin, &gWorldDelta, &gWorldRay.origin);
- Q3Ellipsoid_SetOrigin(gBallGeo, &gWorldRay.origin);
-
- /* Set ray again */
- status = Q3WorldRayPick_SetRay(gPick, &gWorldRay);
- DEBUG_ASSERT(status == kQ3Success, Q3WorldRayPick_SetRay);
-
- return status;
- }
-
-
- #pragma mark -
-
- /*
- * Pick_InitializeWorldRay
- *
- * Create an world delta vector with a random direction with a
- * magnitude that controls the rate. Normalize this vector to
- * make the actual world ray. The world ray is initially positioned
- * at the origin.
- */
- static
- TQ3Status Pick_InitializeWorldRay(
- TQ3PickObject pickObj,
- TQ3Ray3D *pWorldRay,
- TQ3Vector3D *pWorldDelta)
- {
- TQ3Status status = kQ3Failure;
-
- Q3Point3D_Set(&pWorldRay->origin, 0.0, 0.0, 0.0);
-
- /* Generate a random vector in XY plane */
- Q3Vector3D_Set(pWorldDelta, System_RandomFloat(), System_RandomFloat(), 0.0);
-
- Q3Vector3D_Normalize(pWorldDelta, &pWorldRay->direction);
-
- /* Set world ray's origin and direction */
- status = Q3WorldRayPick_SetRay(pickObj, pWorldRay);
- DEBUG_ASSERT(status == kQ3Success, Q3WorldRayPick_SetRay);
-
- return status;
- }
-
-
- /*
- * Pick_ComputeReflectedWorldRay
- *
- * Given: pWorldDelta
- * pRay->direction (a normalized pWorldDelta)
- * normal
- *
- * Find: A new pWorldDelta with a negative angle relative to the angle
- * between delta and normal.
- *
- * ++
- * /!|\
- * / !| \
- * / N!| \
- * D / !| \ D'
- * / V| \
- * / | \
- * / D1| \
- * v<----- V ----->v
- * D2 D2'
- *
- * ! N = Normal
- * / D = Delta
- * | D1 = Delta component
- * - D2 = Delta component
- * \ D' = Reflection of D
- * c = scalar for D projection onto N
- *
- * D • N
- * c = ----- but since N • N = 1 then c = D • N
- * N • N
- *
- * D = D1 + D2
- * D1 = cN
- *
- * D = cN + D2 therefore
- * D2 = D - cN
- *
- * D2' = Negate(D2)
- * D' = D1 + D2'
- */
- static
- TQ3Status Pick_ComputeReflectedWorldRay(
- TQ3PickObject pickObj,
- TQ3Ray3D *pWorldRay,
- TQ3Vector3D *pWorldDelta)
- {
- TQ3Status status;
- TQ3Vector3D normal,
- newDelta,
- delta1,
- delta2;
- float c;
-
- status = Q3Pick_GetPickDetailData (pickObj, kFirstHit,
- kQ3PickDetailMaskNormal, &normal);
- DEBUG_ASSERT(status == kQ3Success, Q3Pick_GetPickDetailData);
- if (status == kQ3Failure) {
- return status;
- }
-
- /* We negate pWorldDelta to reverse it's direction giving us the newDelta */
- Q3Vector3D_Negate(pWorldDelta, &newDelta);
-
- /* Compute delta1 by projecting delta onto normal */
- c = Q3Vector3D_Dot(&newDelta, &normal);
-
- /* Compensate if ray hits backside of face somehow */
- if (c < 0.0) {
- c = -c;
- }
-
- Q3Vector3D_Scale(&normal, c, &delta1);
- Q3Vector3D_Subtract(&newDelta, &delta1, &delta2);
-
- /* Compute deltaPrime from delta1 and negated delta2. This is delta reflected through the normal */
- Q3Vector3D_Negate(&delta2, &delta2);
- Q3Vector3D_Add(&delta1, &delta2, pWorldDelta);
-
- /* Make sure pick ray is normalized */
- Q3Vector3D_Normalize(pWorldDelta, &pWorldRay->direction);
-
- return status;
- }
-
-
- #pragma mark -
-
- /*
- * Pick_CreateMovingObject
- */
- static
- TQ3GeometryObject Pick_CreateMovingObject(
- TDocumentPtr pDoc,
- TQ3Ray3D *pRay,
- TQ3GroupPosition *pGroupPosn)
- {
- TQ3EllipsoidData data;
- TQ3GeometryObject geometry = NULL;
- TQ3AttributeSet attr = NULL;
- TQ3GroupObject group = NULL;
-
- #define kDefaultObjectColor 0.8, 0.8, 0.0
-
- data.origin = pRay->origin;
-
- Q3Vector3D_Set(&data.orientation, 0.0, kBallRadius, 0.0);
- Q3Vector3D_Set(&data.majorRadius, 0.0, 0.0, kBallRadius);
- Q3Vector3D_Set(&data.minorRadius, kBallRadius, 0.0, 0.0);
-
- data.uMin = data.vMin = 0.0f;
- data.uMax = data.vMax = 1.0f;
- data.caps = kQ3EndCapNone;
-
- data.interiorAttributeSet = NULL;
- data.ellipsoidAttributeSet = NULL;
-
- attr = Q3AttributeSet_New ();
- if (attr != NULL) {
- TQ3ColorRGB color = { kDefaultObjectColor };
-
- data.ellipsoidAttributeSet = attr;
- Q3AttributeSet_Add(attr, kQ3AttributeTypeDiffuseColor, &color);
-
- geometry = Q3Ellipsoid_New(&data);
- DEBUG_ASSERT(geometry != NULL, Q3Ellipsoid_New);
- Object_Dispose_NULL(&attr);
- }
-
- /* Put geometry in an unpickable group */
- group = Q3OrderedDisplayGroup_New();
- DEBUG_ASSERT(group != NULL, Q3OrderedDisplayGroup_New);
-
- if (group != NULL) {
- TQ3DisplayGroupState state;
-
- if (Q3DisplayGroup_GetState(group, &state) == kQ3Success) {
- state &= ~kQ3DisplayGroupStateMaskIsPicked;
-
- Q3DisplayGroup_SetState(group, state);
- }
-
- /* Add geometry to group but don't decrement reference */
- Q3Group_AddObject(group, geometry);
-
- /* Add group to main group */
- *pGroupPosn = Q3Group_AddObject(pDoc->fModel, group);
- Object_Dispose_NULL(&group);
- }
- else {
- /* Error */
- Object_Dispose_NULL(&geometry);
- }
-
- return geometry;
- }
-
-
- /*
- * Pick_GetNearestObject
- *
- * Find and return object hit if it's within minDistance.
- *
- * In this sample code since we're picking with a single ray
- * and our moving object is several units wide portions of
- * this object may pass through other objects because we're
- * testing with a ray cast from the center of our moving object
- * rather than near it's outer extents.
- */
- static
- TQ3Status Pick_GetNearestObject(
- TQ3PickObject pickObj,
- float minDistance,
- TQ3GeometryObject *pGeoHit,
- TQ3GeometryObject *pPrevGeoHit)
- {
- TQ3Status status = kQ3Failure;
- TQ3PickDetail pickDetailValidMask;
- float distance;
- TQ3GeometryObject objectHit = NULL;
-
- status = Q3Pick_GetPickDetailValidMask(pickObj, kFirstHit, &pickDetailValidMask);
-
- /* Get distance from ray to intersected geometry */
- if (HAS_Distance(pickDetailValidMask)) {
-
- status = Q3Pick_GetPickDetailData (pickObj, kFirstHit,
- kQ3PickDetailMaskDistance, &distance);
- DEBUG_ASSERT(status == kQ3Success, Q3Pick_GetPickDetailData);
-
- /* Is this geometry within "minDistance"? */
- if (distance <= minDistance) {
-
- if (HAS_Object(pickDetailValidMask)) {
- /* Get the object */
- status = Q3Pick_GetPickDetailData (pickObj, kFirstHit,
- kQ3PickDetailMaskObject, &objectHit);
-
- if (objectHit == *pGeoHit) {
- /* We've hit the same geometry */
- Object_Dispose_NULL(&objectHit);
- }
- else {
- /* Return new geometry hit */
- *pPrevGeoHit = *pGeoHit; /* Need to unhighlight this if non-NULL */
- *pGeoHit = objectHit;
- }
- }
- }
- else {
- *pPrevGeoHit = *pGeoHit; /* Need to unhighlight this if non-NULL */
- *pGeoHit = NULL;
- }
- }
-
- return status;
- }
-
-
- /*
- * Pick_HighlightObject
- */
- static
- TQ3Status Pick_HighlightObject(
- TDocumentPtr pDoc,
- TQ3GeometryObject geoObj,
- TQ3Boolean doHighlight)
- {
- TQ3Status status = kQ3Failure;
- TQ3AttributeSet attr = NULL;
- TQ3Switch isHighlighted = kQ3On;
- TQ3Boolean doesContain,
- doUpdate = kQ3False;
-
- DEBUG_ASSERT(pDoc != NULL, Pick_HighlightObject);
- DEBUG_ASSERT(geoObj != NULL, Pick_HighlightObject);
-
- status = Q3Geometry_GetAttributeSet(geoObj, &attr);
- DEBUG_ASSERT(status == kQ3Success, Q3Geometry_GetAttributeSet);
- DEBUG_ASSERT(attr != NULL, Q3Geometry_GetAttributeSet__attr);
-
- if (status == kQ3Success) {
- if (attr != NULL) {
- doesContain = Q3AttributeSet_Contains(
- attr, kQ3AttributeTypeHighlightState);
-
- /* Add highlight if geometry doesn't contain one */
- if (doHighlight == kQ3True) {
- if (doesContain == kQ3False) {
- Q3AttributeSet_Add(
- attr, kQ3AttributeTypeHighlightState, &isHighlighted);
- doUpdate = kQ3True;
- }
- }
- else {
- /* Remove highlight if there is one */
- if (doesContain == kQ3True) {
- Q3AttributeSet_Clear(
- attr, kQ3AttributeTypeHighlightState);
- doUpdate = kQ3True;
- }
- }
-
- if (doUpdate == kQ3True) {
- status = Q3Geometry_SetAttributeSet(geoObj, attr);
- DEBUG_ASSERT(status == kQ3Success, Q3Geometry_SetAttributeSet);
-
- Document_Draw(pDoc);
- }
- }
- }
- Object_Dispose_NULL(&attr);
-
- return status;
- }
-
-
-
- #pragma mark -
-
- /*
- * Pick_CreateLine
- */
- TQ3GeometryObject Pick_CreateLine(
- TQ3Point3D *pXYZ,
- TQ3Vector3D *pNormal,
- float scale,
- unsigned long colorIndex)
- {
- TQ3LineData lineData;
- TQ3GeometryObject lineObject = NULL;
- TQ3AttributeSet attr = NULL;
- TQ3Vector3D scaledDir;
-
- DEBUG_ASSERT(colorIndex < sizeof(rgbColors)/sizeof(TQ3ColorRGB), Pick_CreateLine);
-
- if (pNormal != NULL) {
- scaledDir = *pNormal;
- Q3Vector3D_Scale(pNormal, scale, &scaledDir);
-
- /*
- * Create a line representing the normal vector at the point xyz
- */
- lineData.vertices[0].point.x = pXYZ->x;
- lineData.vertices[0].point.y = pXYZ->y;
- lineData.vertices[0].point.z = pXYZ->z;
- lineData.vertices[0].attributeSet = NULL;
-
- lineData.vertices[1].point.x = pXYZ->x + scaledDir.x;
- lineData.vertices[1].point.y = pXYZ->y + scaledDir.y;
- lineData.vertices[1].point.z = pXYZ->z + scaledDir.z;
- lineData.vertices[1].attributeSet = NULL;
-
- attr = Q3AttributeSet_New ();
- if (attr != NULL) {
-
- lineData.lineAttributeSet = attr;
- (void) Q3AttributeSet_Add (attr, kQ3AttributeTypeDiffuseColor,
- &rgbColors[colorIndex]);
-
- lineObject = Q3Line_New(&lineData);
- Object_Dispose_NULL(&attr);
- }
- }
- else {
- TQ3GroupObject crossHairGroup = NULL;
-
- crossHairGroup = Q3OrderedDisplayGroup_New();
- if (crossHairGroup != NULL) {
- #define kHalfLength (kCylRadius / 2.0)
-
- /*
- * Create a horiztonal line at the point xyz
- */
- lineData.vertices[0].point.x = pXYZ->x - kHalfLength;
- lineData.vertices[1].point.x = pXYZ->x + kHalfLength;
- lineData.vertices[0].point.y =
- lineData.vertices[1].point.y = pXYZ->y;
- lineData.vertices[0].point.z =
- lineData.vertices[1].point.z = pXYZ->z;
- lineData.vertices[0].attributeSet = NULL;
- lineData.vertices[1].attributeSet = NULL;
-
- attr = Q3AttributeSet_New ();
- if (attr != NULL) {
- lineData.lineAttributeSet = attr;
- (void) Q3AttributeSet_Add (attr, kQ3AttributeTypeDiffuseColor,
- &rgbColors[colorIndex]);
-
- lineObject = Q3Line_New(&lineData);
- Object_Dispose_NULL(&attr);
-
- (void) Q3Group_AddObject(crossHairGroup, lineObject);
- Object_Dispose_NULL(&lineObject);
- }
-
- /*
- * Create a vertical line at the point xyz
- */
- lineData.vertices[0].point.x =
- lineData.vertices[1].point.x = pXYZ->x;
- lineData.vertices[0].point.y = pXYZ->y - kHalfLength;
- lineData.vertices[1].point.y = pXYZ->y + kHalfLength;
- lineData.vertices[0].point.z =
- lineData.vertices[1].point.z = pXYZ->z;
- lineData.vertices[0].attributeSet = NULL;
- lineData.vertices[1].attributeSet = NULL;
-
- attr = Q3AttributeSet_New ();
- if (attr != NULL) {
- lineData.lineAttributeSet = attr;
- (void) Q3AttributeSet_Add (attr, kQ3AttributeTypeDiffuseColor,
- &rgbColors[colorIndex]);
-
- lineObject = Q3Line_New(&lineData);
- Object_Dispose_NULL(&attr);
-
- (void) Q3Group_AddObject(crossHairGroup, lineObject);
- Object_Dispose_NULL(&lineObject);
- }
-
- lineObject = crossHairGroup;
- }
- else {
- DEBUG_ASSERT(0, "Pick_CreateLine: Q3OrderedDisplayGroup_New failed.");
- }
- }
-
- return lineObject;
- }
-
-
- /*
- * Pick_DrawRayAndNormal
- */
- TQ3Status Pick_DrawRayAndNormal(
- TDocumentPtr pDoc,
- TQ3Ray3D *pTestRay,
- TQ3GroupObject normalGroup,
- unsigned long numHits)
- {
- TQ3Status status;
- TQ3PickDetail pickDetailValidMask;
- TQ3Point3D xyzPoint;
- TQ3Vector3D normal;
- float distance;
- TQ3GeometryObject normalObject = NULL,
- rayObject = NULL;
- TQ3Object tempObject = NULL;
- TQ3GroupPosition normalPosn = NULL,
- rayPosn = NULL,
- normalGroupPosn = NULL;
- TQ3Boolean hasXYZandNormal,
- hasXYZOnly;
-
- if (numHits >= 1) {
- status = Q3Pick_GetPickDetailValidMask(gPick, kFirstHit, &pickDetailValidMask);
- DEBUG_ASSERT(status != kQ3Failure, Q3Pick_GetPickDetailValidMask);
-
- hasXYZOnly = HAS_XYZ(pickDetailValidMask)
- ? kQ3True
- : kQ3False;
- if (hasXYZOnly) {
- status = Q3Pick_GetPickDetailData (gPick, kFirstHit,
- kQ3PickDetailMaskXYZ, &xyzPoint);
- DEBUG_ASSERT(status != kQ3Failure, Q3Pick_GetPickDetailData);
- }
-
- hasXYZandNormal = (HAS_Normal(pickDetailValidMask) && hasXYZOnly)
- ? kQ3True
- : kQ3False;
- if (hasXYZandNormal) {
- hasXYZOnly = kQ3False;
-
- status = Q3Pick_GetPickDetailData (gPick, kFirstHit,
- kQ3PickDetailMaskNormal, &normal);
- DEBUG_ASSERT(status != kQ3Failure, Q3Pick_GetPickDetailData);
- }
- }
- else {
- hasXYZandNormal = kQ3False;
- hasXYZOnly = kQ3False;
- }
-
- if (hasXYZandNormal == kQ3True) {
- /*
- * Add representation of normal and ray to
- * main normal group then remove them again
- */
- normalObject = Pick_CreateLine(
- &xyzPoint,
- &normal,
- 2.0 * kCylRadius,
- k_Magenta);
- if (normalObject == NULL) {
- return kQ3Failure;
- }
-
- /* Add normal object to normalGroup */
- normalPosn = Q3Group_AddObject(normalGroup, normalObject);
- Object_Dispose_NULL(&normalObject);
- }
- else
- if (hasXYZOnly == kQ3True) {
- /*
- * Add representation of intersection cross hairs to
- * main normal group then remove them again
- */
- normalObject = Pick_CreateLine(
- &xyzPoint,
- NULL,
- 1.0,
- k_Cyan);
- if (normalObject == NULL) {
- return kQ3Failure;
- }
-
- /* Add normal object to normalGroup */
- normalPosn = Q3Group_AddObject(normalGroup, normalObject);
- Object_Dispose_NULL(&normalObject);
- }
-
-
- /* Make a line from ray's origin to intersection point */
- if (HAS_Distance(pickDetailValidMask)) {
- status = Q3Pick_GetPickDetailData (gPick, kFirstHit,
- kQ3PickDetailMaskDistance, &distance);
- DEBUG_ASSERT(status == kQ3Success, Q3Pick_GetPickDetailData);
- }
-
- /* Scale test ray so we can see it */
- rayObject = Pick_CreateLine(
- &pTestRay->origin,
- &pTestRay->direction,
- distance,
- (numHits == 1)
- ? k_Green
- : k_Red);
- if (rayObject == NULL) {
- Object_Dispose_NULL(&normalObject);
- return kQ3Failure;
- }
-
- /* Add ray object to normalGroup */
- rayPosn = Q3Group_AddObject(normalGroup, rayObject);
- Object_Dispose_NULL(&rayObject);
-
- /* Add normalGroup to model */
- normalGroupPosn = Q3Group_AddObject(pDoc->fModel, normalGroup);
-
- /* Draw model with added lines */
- Document_Draw(pDoc);
-
- /* Remove normal and ray objects from normalGroup, normalGroup from model */
- if (normalPosn != NULL) {
- tempObject = Q3Group_RemovePosition (normalGroup, normalPosn);
- Object_Dispose_NULL(&tempObject);
- }
-
- if (rayPosn != NULL) {
- tempObject = Q3Group_RemovePosition (normalGroup, rayPosn);
- Object_Dispose_NULL(&tempObject);
- }
-
- if (normalGroupPosn != NULL) {
- tempObject = Q3Group_RemovePosition (pDoc->fModel, normalGroupPosn);
- Object_Dispose_NULL(&tempObject);
- }
-
- return kQ3Success;
- }
-